package per.destiny.seckill.controller;

import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import per.destiny.seckill.mq.SeckillProducer;
import per.destiny.seckill.pojo.SeckillOrder;
import per.destiny.seckill.pojo.User;
import per.destiny.seckill.service.GoodsService;
import per.destiny.seckill.service.OrderService;
import per.destiny.seckill.service.SeckillOrderService;
import per.destiny.seckill.vo.GoodsVo;
import per.destiny.seckill.vo.RespBean;
import per.destiny.seckill.vo.RespBeanEnum;

import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Controller
@RequestMapping("/seckill")
public class SeckillController implements InitializingBean {

    @Autowired
    private GoodsService goodsService;

    @Autowired
    private SeckillOrderService seckillOrderService;

    @Autowired
    private OrderService orderService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SeckillProducer seckillProducer;

    /**
     * 内存标记所用(减少Redis的访问)，key为商品id，value为是否空库存
     */
    private Map<Long, Boolean> EMPTY_REDIS_STOCK_MAP = new ConcurrentHashMap<>();

    @RequestMapping(value = "/doSeckill", method = RequestMethod.POST)
    @ResponseBody
    public RespBean toList(User user, Long goodsId) {
        if (user == null) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        Boolean isGoodsStockEmpty = EMPTY_REDIS_STOCK_MAP.get(goodsId);
        if (isGoodsStockEmpty) {
            // 内存标记，减少Redis的访问
            return RespBean.error(RespBeanEnum.EMPTY_SOCKET);
        }

        ValueOperations valueOperations = redisTemplate.opsForValue();
        // 判断是否重复抢购
        SeckillOrder seckillOrder = (SeckillOrder) valueOperations.get("order:" + user.getId() + ":" + goodsId);
        if (seckillOrder != null) {
            return RespBean.error(RespBeanEnum.REPEATE_ERROR);
        }
        // 预减库存（stock为执行后的值）
        Long stock = valueOperations.decrement("seckillGoods:" + goodsId);
        if (stock < 0) {
            // 内存标记设置 空库存标记
            EMPTY_REDIS_STOCK_MAP.put(goodsId, true);
            // 这个操作很有必要。
            valueOperations.increment("seckillGoods:" + goodsId);
            return RespBean.error(RespBeanEnum.EMPTY_SOCKET);
        }
        //通过MQ（推荐用RabbitMQ，Kafka，RocketMQ）异步下单
        // 【1. 处理消息如果库存足够的情况，必须要保证成功，否则会出现少卖的情况 2. 或者消息处理失败后直接修改内存标记（记住要分布式修改，使用ZK或者MQ解决）和Redis标记】
        seckillProducer.sendSeckillMessage(user.getId(), goodsId);
        return RespBean.success(0);
    }

    /**
     * 获取秒杀结果
     *
     * @param user
     * @param goodsId
     * @return orderId:成功， -1表示秒杀失败，0表示排队中
     */
    @RequestMapping(value = "/getResult", method = RequestMethod.GET)
    @ResponseBody
    public RespBean getResult(User user, Long goodsId) {
        if (user == null) {
            return RespBean.error(RespBeanEnum.SESSION_ERROR);
        }
        Long orderId = seckillOrderService.getResult(user.getId(), goodsId);
        return RespBean.success(orderId);
    }

    /**
     * 将商品库存数量加载到Redis中
     *
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        List<GoodsVo> goodsVos = goodsService.findGoodsVo();
        if (CollectionUtils.isEmpty(goodsVos)) {
            return;
        }
        goodsVos.forEach(goodsVo -> {
            EMPTY_REDIS_STOCK_MAP.put(goodsVo.getId(), false);
            redisTemplate.opsForValue().set("seckillGoods:" + goodsVo.getId(), goodsVo.getStockCount());
        });
    }
}
